﻿using System;
using System.Collections.Generic;
namespace IFramework
{
   
    public class FSMachine : IFSM
    {
        private string name;
        private IFSMState anyStatusState;
        private IFSMState exitState;
        private IFSMState startState;
        private IFSMState currentState;
        public string Name { get { return name; } }
        public IFSMState AnyStatusState { get { return anyStatusState; } }
        public IFSMState ExitState { get { return exitState; } }
        public IFSMState StartState { get { return startState; } }
        public IFSMState CurrentState { get { return currentState; } }


        private Dictionary<Type, Dictionary<string, ITransitionCondtion>> condtions;
        private Dictionary<IFSMState, IFSMStateInfo> stateInfo;
        
        public FSMachine(string name)
        {
            this.name = name;
            condtions = new Dictionary<Type, Dictionary<string, ITransitionCondtion>>();
            stateInfo = new Dictionary<IFSMState, IFSMStateInfo>();
        }

        public IFSMStateInfo SetExitState(IFSMState state, FSMStateType StateType)
        {
            IFSMStateInfo info = CreateState(state, StateType);
            if (state == null) throw new Exception("State Is Null");
            exitState = state;
            return info;
        }
        public IFSMStateInfo SetStartState(IFSMState state, FSMStateType StateType)
        {
            IFSMStateInfo info= CreateState(state, StateType);
            if (state == null) throw new Exception("State Is Null");
            startState = state;
            return info;
        }

        public IFSMStateInfo SetAnyStatusState(IFSMState state, FSMStateType StateType)
        {
            IFSMStateInfo info = CreateState(state, StateType);
            if (state == null) throw new Exception("State Is Null");
            anyStatusState = state;
            return info;
        }

        //T int float String Bool
        public ITransitionCondtion CreateCondition<T>(string name, T defaultVal, TransitionCondtionType condtionType) 
        {
            ITransitionCondtion condtion;
            if (typeof(T) == typeof(bool))
                condtion = new BoolTransitionCondtion(name, defaultVal, condtionType);
            else if (typeof(T) == typeof(string))
                condtion = new StringTransitionCondtion(name, defaultVal, condtionType);
            else if (typeof(T) == typeof(float))
                condtion = new FloatTransitionCondtion(name, defaultVal, condtionType);
            else if (typeof(T) == typeof(int))
                condtion = new IntTransitionCondtion(name, defaultVal, condtionType);
            else
                throw new Exception("Fault TYpe Of T");
            return condtion;
        }

        public StateTransition CreateTransition(StateTransitionType type,IFSMState head,IFSMState trail)
        {
            StateTransition transition= new StateTransition(type);
            transition.SetHeadState(head);
            transition.SetTrailState(head);
            return transition;
        }
        public void BindCondition(StateTransition transition, ITransitionCondtion condtion)
        {
            if (!condtions.ContainsKey(condtion.ObjType))
                condtions.Add(condtion.ObjType, new Dictionary<string, ITransitionCondtion>());
            if (!condtions[condtion.ObjType].ContainsKey(condtion.CondtionName))
                condtions[condtion.ObjType].Add(condtion.CondtionName, condtion);
            else
                Log.E("Condition Exit " + condtion.CondtionName);
            transition.BindCondition(condtion);
        }
        public void BindConditions(StateTransition transition, ITransitionCondtion[] condtion)
        {
            for (int i = 0; i < condtion.Length; i++)
            {
                if (!condtions.ContainsKey(condtion[i].ObjType))
                    condtions.Add(condtion[i].ObjType, new Dictionary<string, ITransitionCondtion>());
                if (!condtions[condtion[i].ObjType].ContainsKey(condtion[i].CondtionName))
                    condtions[condtion[i].ObjType].Add(condtion[i].CondtionName, condtion[i]);
                else
                    Log.E("Condition Exit " + condtion[i].CondtionName);
                transition.BindCondition(condtion[i]);
            }
        }
        public void BindCustomCondition(StateTransition transition, Func<bool> condtion)
        {
            transition.BindCustomCondition(condtion);
        }

        public IFSMStateInfo CreateState(IFSMState state,FSMStateType StateType)
        {
            IFSMStateInfo info = new IFSMStateInfo(state, StateType);
            if (!stateInfo.ContainsKey(state))
                stateInfo.Add(state, new IFSMStateInfo(state, StateType));
            return info;
        }
        public IFSMStateInfo BindTransition(IFSMStateInfo info, StateTransition transition)
        {
            switch (transition.TransitionType)
            {
                case StateTransitionType.AutoNextState:
                    info.SetAutoNextTransition(transition);
                    break;
                case StateTransitionType.WithCondition:
                    info.SetConditionTransition(transition);
                    break;
            }
            return info;
        }
        public IFSMStateInfo BindTransitions(IFSMStateInfo info, StateTransition[] transitions)
        {
            for (int i = 0; i < transitions.Length; i++)
            {
                switch (transitions[i].TransitionType)
                {
                    case StateTransitionType.AutoNextState:
                        info.SetAutoNextTransition(transitions[i]);
                        break;
                    case StateTransitionType.WithCondition:
                        info.SetConditionTransition(transitions[i]);
                        break;
                }
            }
            return info;
        }

        private void LoopNext()
        {
            if (stateInfo[currentState].AutoNextIStateTransition == null) return;
            currentState = stateInfo[currentState].AutoNextIStateTransition.GoToNextState();
            LoopNext();
        }

        public void Run()
        {
            if (startState == null) throw new Exception("StartState Is Null");
            startState.OnEnter();
            currentState = startState;
            LoopNext();
        }
        public void Stop()
        {
            if (exitState == null) return;
            currentState = exitState;
            exitState.OnEnter();
            LoopNext();
        }

        //有待实现 1:thread 2:mono 3:Timer
        public void Update()
        {
            if (stateInfo[currentState].StateType == FSMStateType.AutoLoop)
            {
                currentState.Update();
            }
        }


        private void CheckLeagal(Type condtionObjType, string ConditionName)
        {
            if (!condtions.ContainsKey(condtionObjType))
                throw new Exception("Illeagal Type " + condtionObjType);
            if (!condtions[condtionObjType].ContainsKey(ConditionName))
                throw new Exception("Illeagal Name " + ConditionName);
        }
        public void SetBool(string ConditionName, bool value)
        {
            CheckLeagal(typeof(bool), ConditionName);
            BoolTransitionCondtion condtion = condtions[typeof(bool)][ConditionName] as BoolTransitionCondtion;
            condtion.SetValue(value);
            CanTransitionToNextState();
        }
        public void SetInt(string ConditionName, int value)
        {
            CheckLeagal(typeof(int), ConditionName);
            IntTransitionCondtion condtion = condtions[typeof(int)][ConditionName] as IntTransitionCondtion;
            condtion.SetValue(value);
            CanTransitionToNextState();
        }
        public void SetFloat(string ConditionName, float value)
        {
            CheckLeagal(typeof(float), ConditionName);
            FloatTransitionCondtion condtion = condtions[typeof(float)][ConditionName] as FloatTransitionCondtion;
            condtion.SetValue(value);
            CanTransitionToNextState();
        }
        public void SetString(string ConditionName, string value)
        {
            CheckLeagal(typeof(string), ConditionName);
            StringTransitionCondtion condtion = condtions[typeof(string)][ConditionName] as StringTransitionCondtion;
            condtion.SetValue(value);
            CanTransitionToNextState();
        }

        private void CanTransitionToNextState()
        {
            IFSMState tempState = null;
            List<StateTransition> transitions = stateInfo[currentState].ConditionTransitions;
            for (int i = 0; i < transitions.Count; i++)
            {
                if (transitions[i].IsMetCondition)
                {
                    StateTransition transition = transitions[i];
                    tempState = transition.GoToNextState();
                    break;
                }
            }
            if (tempState != null)
            {
                currentState = tempState;
                LoopNext();
            }
        }


        private void RunAnyStatusState()
        {
            if (anyStatusState == null) return;
            IFSMState tempState = null;
            List<StateTransition> transitions = stateInfo[anyStatusState].ConditionTransitions;
            for (int i = 0; i < transitions.Count; i++)
            {
                if (transitions[i].IsMetCondition)
                {
                    StateTransition transition = transitions[i];
                    tempState = transition.GoToNextState();
                    break;
                }
            }
            if (tempState != null)
            {
                currentState = tempState;
                LoopNext();
            }
        }
    }
}
